//
//  LLTVideoEdit.swift
//  mmda
//
//  Created by 李陆涛 on 2018/8/13.
//  Copyright © 2018年 mmda. All rights reserved.
//

import UIKit
import AVFoundation
import Photos

class LLTVideoEdit: BaseVC {
    
    var operationCellKey = "operationCellKey"
    
    var videourl:URL?
    var maxEditVideoTime:CGFloat = 15
    
    
    private var bottomView:UIView!
    private var indicatorLine:UIView!
    
    private var cancelBtn:UIButton!
    private var doneBtn:UIButton!
    
    private var timer:Timer?
    
    private var offsetX:CGFloat = 0
    
    private var orientationChanged:Bool = false
    
    private var asset:AVAsset?
    private var passet:PHAsset?
    
    private var interval:TimeInterval = 0
    
    private var measureCount = 0
    
    private var queue:OperationQueue!
    
    private var imageCache = [String:UIImage]()
    private var opCache = [String:BlockOperation]()
    
    
    private var playerLayer:AVPlayerLayer!
    private var collectionView : UICollectionView!
    private var editView:LLTEditFrameView!
    private var generator:AVAssetImageGenerator!
    
    var completeHander:((_ image:UIImage?,_ videoURL:URL? ,_ success:Bool) -> Void)?
    
    public convenience init(maxDuration duration:CGFloat, vedio url:URL?, phAsset:PHAsset?) {
        self.init()
        maxEditVideoTime = duration
        videourl = url
        passet = phAsset
    }
    
    deinit {
        
        print("------------LLTVideoEdit")
        
        stopTimer()
        queue.cancelAllOperations()
        
        NotificationCenter.default.removeObserver(self)
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupUI()
        
        if let url = videourl {
            
            asset = AVAsset(url: url)
        }
        
        
        analysisAssetImages()
        setupQueue()
        setupGenerator()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        var inset = UIEdgeInsets.zero
        
        if #available(iOS 11.0, *) {
            inset = self.view.safeAreaInsets
        }
        
        
        let count = CGFloat(min(measureCount, 10))
        
        playerLayer.frame = CGRect(x: 15, y: inset.top > 0 ? Int(inset.top) : 30, width: kScreenWidth - 30, height: kScreenHeight - 160 - Int(inset.bottom))
        
        editView.frame = CGRect(x: (CGFloat(kScreenWidth) - kItemWidth * 10) / 2 , y: CGFloat(kScreenHeight) - 100 - inset.bottom, width: kItemWidth * count, height: kItemHeight)
        editView.validRect = editView.bounds
        
        collectionView.frame = CGRect(x: inset.left, y: CGFloat(kScreenHeight) - 100 - inset.bottom , width: CGFloat(kScreenWidth) - inset.left - inset.right, height: kItemHeight)
        
        let leftOffset = (CGFloat(kScreenWidth) - kItemWidth * 10) / 2 - inset.left
        let rightOffset = (CGFloat(kScreenWidth) - kItemWidth * 10) / 2 - inset.right
        
        collectionView.contentInset = UIEdgeInsetsMake(0, leftOffset, 0, rightOffset)
        collectionView.setContentOffset(CGPoint(x: offsetX - leftOffset, y: 0), animated: false)
        
        let bottomViewH = 44;
        let bottomBtnH = 30;
        
        bottomView.frame = CGRect(x: 0, y: kScreenHeight - bottomViewH - Int(inset.bottom), width: kScreenWidth, height: Int(kItemHeight))
        
        cancelBtn.frame = CGRect(x: Int(10 + inset.left), y: 7, width: 60, height: bottomBtnH)
        
        doneBtn.frame = CGRect(x: kScreenWidth - 70 - Int(inset.right), y: 7, width: 60, height: bottomBtnH)
        
        if asset != nil {
            
            self.startTimer()
        }
        
    }
    
    private func setupUI() {
        
        self.view.backgroundColor = UIColor.black
        
        playerLayer = AVPlayerLayer()
        self.view.layer.addSublayer(playerLayer)
        
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: kItemWidth, height: kItemHeight)
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        
        collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.register(LLTEditVideoCell.self, forCellWithReuseIdentifier: "LLTEditVideoCell")
        
        self.view.addSubview(collectionView)
        
        creatBottomView()
        
        editView = LLTEditFrameView()
        self.view.addSubview(editView)
        editViewAction()
        
        
        indicatorLine = UIView(frame: CGRect(x: 0, y: 0, width: 2, height: kItemHeight))
        indicatorLine.backgroundColor = UIColor.white.withAlphaComponent(0.7)
    }
    
    private func setupQueue() {
        
        queue = OperationQueue()
        queue.maxConcurrentOperationCount = 3
        
        NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationChanged), name: NSNotification.Name.UIApplicationWillChangeStatusBarOrientation, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(appResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(appBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
    }
    
    private func setupGenerator() {
        
        if let asset = asset {
            
            generator = AVAssetImageGenerator(asset: asset)
            generator.maximumSize = CGSize(width: kItemWidth * 4, height: kItemHeight*4)
            generator.appliesPreferredTrackTransform = true
            generator.requestedTimeToleranceBefore = kCMTimeZero
            generator.requestedTimeToleranceAfter = kCMTimeZero
            generator.apertureMode = .productionAperture
        }
    }
    
    private func creatBottomView() {
        
        bottomView = UIView()
        
        bottomView.backgroundColor = UIColor.black.withAlphaComponent(0.7)
        self.view.addSubview(bottomView)
        
        cancelBtn = UIButton(type: .custom)
        cancelBtn.titleLabel?.font = kFont14
        cancelBtn.setTitleColor(.white, for: .normal)
        cancelBtn.setTitle("取消", for: .normal)
        cancelBtn.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
        bottomView.addSubview(cancelBtn)
        
        
        doneBtn = UIButton(type: .custom)
        doneBtn.titleLabel?.font = kFont14
        doneBtn.setTitleColor(.white, for: .normal)
        doneBtn.setTitle("完成", for: .normal)
        doneBtn.layer.masksToBounds = true
        doneBtn.layer.cornerRadius = 3.0
        doneBtn.addTarget(self, action: #selector(doneAction), for: .touchUpInside)
        bottomView.addSubview(doneBtn)
    }
    
    private func analysisAssetImages() {
        
        if let passet = passet {
            
            let duration = passet.duration
            
            interval = TimeInterval(maxEditVideoTime / 10.0)
            
            measureCount = Int(duration / interval)
            
            
            PHCachingImageManager.default().requestPlayerItem(forVideo: passet, options: nil) {
                
                [weak self] item, info in
                guard let `self` = self else { return }
                
                
                guard let item = item else { return }
                
                DispatchQueue.main.async {
                    
                    let player = AVPlayer(playerItem: item)
                    self.playerLayer.player = player
                    
                    self.startTimer()
                }
                
            }
            
            PHCachingImageManager.default().requestAVAsset(forVideo: passet, options: nil) {
                
                [weak self] asset, _, _ in
                guard let `self` = self else { return }
                
                DispatchQueue.main.async {
                    
                    self.asset = asset
                    
                    self.setupGenerator()
                    
                    self.collectionView.reloadData()
                }
            }
        } else {
            
            let duration = CMTimeGetSeconds(asset!.duration)
            
            interval = TimeInterval(maxEditVideoTime / 10.0)
            
            measureCount = Int(duration / interval)
            
            let player = AVPlayer(url: videourl!)
            self.playerLayer.player = player
        }
    }
    
    private func startTimer() {
        
        self.stopTimer()
        
        let duration = interval * TimeInterval(self.editView.validRect.size.width / kItemWidth)
        
        timer = Timer.scheduledTimer(timeInterval: duration, target: self, selector: #selector(playPartVideo), userInfo: nil, repeats: true)
        
        timer?.fire()
        RunLoop.main.add(timer!, forMode: RunLoopMode.commonModes)
        
        indicatorLine.frame = CGRect(x: self.editView.validRect.origin.x, y: 0, width: 2, height: kItemHeight)
        self.editView.addSubview(indicatorLine)
        
        UIView.animate(withDuration: duration, delay: 0, options: [.repeat,.allowUserInteraction,.curveLinear], animations: {
            
            [weak self] in
            guard let `self` = self else { return }
            
            self.indicatorLine.frame = CGRect(x: self.editView.validRect.maxX - 2, y: 0, width: 2, height: kItemHeight)
            }, completion: nil)
    }
    
    private func stopTimer() {
        
        timer?.invalidate()
        timer = nil
        self.playerLayer.player?.pause()
        
        self.indicatorLine.removeFromSuperview()
    }
    
    @objc private func playPartVideo() {
        
        self.playerLayer.player?.play()
        
        self.playerLayer.player?.seek(to: getStartTime(), toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
        
    }
    
    @objc private func deviceOrientationChanged() {
        
        offsetX = self.collectionView.contentOffset.x + self.collectionView.contentInset.left
        orientationChanged = true
    }
    
    @objc private func appResignActive() {
        
        stopTimer()
    }
    
    @objc private func appBecomeActive() {
        
        startTimer()
    }
    
    @objc private func cancelAction() {
        
        
        DispatchQueue.main.async {
            
            [weak self] in
            guard let `self` = self else { return }
            
            self.stopTimer()
            self.completeHander?(nil,nil,false)
            self.dismiss(animated: true, completion: nil)
        }
    }
    
    @objc private func doneAction() {
        
        stopTimer()
        
        self.view.showLoadingHUDText("导出中")
        
        LLTPhotoManager.cropVideo(asset: asset!, cropRange: self.getTimeRange()) {
            [weak self] url, duration, success in
            guard let `self` = self else { return }
            
            DispatchQueue.main.async {
                
                self.view.hideAllToasts()
                self.stopTimer()
                self.playerLayer.player = nil
                self.dismiss(animated: true, completion: nil)
                self.completeHander?(nil,url,true)
            }
        }
    }
    
    
    private func editViewAction() {
        
        editView.editViewValidRectChanged = {
            
            [weak self] in
            guard let `self` = self else { return }
            
            self.stopTimer()
            self.playerLayer.player?.seek(to: self.getStartTime(), toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
            
        }
        
        editView.editViewValidRectEndChanged = {
            
            [weak self] in
            guard let `self` = self else { return }
            
            self.startTimer()
        }
    }
    
    private func getStartTime() -> CMTime {
        
        let rect = self.collectionView.convert(self.editView.validRect, from: self.editView)
        let s = max(0, interval * TimeInterval(rect.origin.x / kItemWidth))
        
        return CMTime(seconds: s, preferredTimescale: (self.playerLayer.player?.currentTime().timescale)!)
    }
    
    private func getTimeRange() -> CMTimeRange {
        
        let start = getStartTime()
        
        let d = interval * TimeInterval(self.editView.validRect.size.width / kItemWidth)
        
        let duration = CMTime(seconds: d, preferredTimescale: (self.playerLayer.player?.currentTime().timescale)!)
        
        return CMTimeRange(start: start, duration: duration)
    }
    
}

extension LLTVideoEdit: UICollectionViewDataSource,UICollectionViewDelegate {
    
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        
        if self.playerLayer.player == nil || orientationChanged {
            
            orientationChanged = false
            
            return
        }
        
        stopTimer()
        self.playerLayer.player?.seek(to: getStartTime(), toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
    }
    
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        
        if !decelerate {
            startTimer()
        }
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        
        startTimer()
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        if asset == nil {
            return 0
        }
        
        return measureCount
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LLTEditVideoCell", for: indexPath) as! LLTEditVideoCell
        
        if imageCache.contains(key: "\(indexPath.row)") {
            
            cell.imageView.image = imageCache["\(indexPath.row)"]
        }
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        
        guard let asset = asset else {
            
            return
        }
        
        if imageCache.contains(key: "\(indexPath.row)") || opCache.contains(key: "\(indexPath.row)")  {
            return
        }
        
        let op = BlockOperation {
            
            [weak self] in
            guard let `self` = self else { return }
            
            let row = indexPath.row
            let i = TimeInterval(row) * self.interval
            
            let time = CMTime(value: CMTimeValue(CMTimeScale(i + 0.35) * asset.duration.timescale), timescale: asset.duration.timescale)
            
            guard let cgImg = try? self.generator.copyCGImage(at: time, actualTime: nil) else {
                
                objc_removeAssociatedObjects(cell)
                return
            }
            
            let image = UIImage(cgImage: cgImg)
            
            self.imageCache["\(indexPath.row)"] = image
            
            DispatchQueue.main.async {
                
                if let nowIndexPath = collectionView.indexPath(for: cell) {
                    
                    if row == nowIndexPath.row {
                        
                        let cell = cell as! LLTEditVideoCell
                        
                        cell.imageView.image = image
                    } else {
                        
                        if self.imageCache.contains(key: "\(nowIndexPath.row)") {
                            
                            let cell = cell as! LLTEditVideoCell
                            
                            cell.imageView.image = self.imageCache["\(nowIndexPath.row)"]
                        }
                    }
                }
                
                self.opCache.removeValue(forKey:"\(row)")
                objc_removeAssociatedObjects(cell);
                
            }
        }
        queue.addOperation(op)
        self.opCache["\(indexPath.row)"] = op
        
        objc_setAssociatedObject(cell, &operationCellKey, op, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    
    func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        
        guard let op = objc_getAssociatedObject(cell, &operationCellKey) as? BlockOperation else {
            return
        }
        
        op.cancel()
        objc_removeAssociatedObjects(cell)
        opCache.removeValue(forKey: "\(indexPath.row)")
    }
}

extension Dictionary {
    
    func contains(key:String) -> Bool {
        
        return self.contains { (key1,value1) -> Bool in
            
            guard let key2 = key1 as? String else {
                
                return false
            }
            
            return key == key2
            
        }
    }
}



